package com.github.hiling.common.utils;

import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.TypeReference;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.web.reactive.function.client.WebClient;

import java.util.Collections;
import java.util.Map;
import java.util.function.Consumer;

/**
 * @author wanghailiang
 */
@Slf4j
@Component
public class HttpUtils {

    @Autowired
    private WebClient.Builder webClientBuilder;

    /**
     * Get请求，会自动填充keys和token到header
     * @param uri 请求地址，如：https://lefull.cn/api?q=x
     * @return 字符串
     */
    public  String get(String uri) {
        return get(uri, null, null,true ,new TypeReference<String>(){});
    }


    /**
     * Get请求，会自动填充keys和token到header
     * @param uri 请求地址，如：https://lefull.cn/api?q=x
     * @param dataType 返回的数据类型，如： new TypeReference<List<ApartmentResp>>() {}
     * @return 返回对应类型，如：List<ApartmentResp>
     */
    public  <T> T get(String uri, TypeReference<T> dataType) {
        return get(uri, null, null,true, dataType);
    }

    public  <T> T get(String uri, Map<String, String> queries, TypeReference<T> dataType) {
        return get(uri, queries, null,true, dataType);
    }

    /**
     * Get请求
     * <pre>
     * {@code
     * // 示例，返回对象ApiResponse<List<Apartment>>
     * ApiResponse<List<Apartment>> response = HttpUtils.get(uri,new TypeReference<ApiResponse<List<Apartment>>>() {});
     * }
     * </pre>
     * @param uri 请求地址，如：https://lefull.cn/api?q=x
     * @param queries 请求参数
     * @param headers 自定义请求头
     * @param addHeaderKeys 是否自动填充上游请求头中有keys或token到当前请求的请求头中
     * @param dataType 返回的数据类型，如：new TypeReference<ApiResponse<List<Apartment>>>() {}
     * @return 返回对应类型，如：List<ApartmentResp>
     */
    public  <T> T get(String uri, Map<String, String> queries, Map<String, String> headers,boolean addHeaderKeys, TypeReference<T> dataType) {
        Assert.isTrue(StringUtils.isNotEmpty(uri), "uri不能为空");

        // 处理Query
        uri = getUriQuery(uri, queries);

        // 处理Header
        Consumer<org.springframework.http.HttpHeaders> headersConsumer = getHeadersConsumer(headers, addHeaderKeys);

        String json = webClientBuilder.build().get()
                .uri(uri)
                .headers(headersConsumer)
                .retrieve().bodyToMono(String.class).block();

        return convertType(dataType, json);
    }

    /**
     * 发送POST请求
     *
     * @param uri 请求的URI
     * @param body 请求的body参数
     * @return 返回请求的响应结果的
     */
    public  String post(String uri, Object body) {
        return post(uri, null, null, true, body, new TypeReference<String>() {});
    }

    /**
     * 发送POST请求
     *
     * @param uri 请求的URI
     * @param body 请求的body参数
     * @param dataType 请求返回的数据类型引用
     * @param <T> 请求返回的数据类型
     * @return 返回请求的响应结果
     */
    public  <T> T post(String uri, Object body, TypeReference<T> dataType) {
        return post(uri, null, null, true, body, dataType);
    }

    /**
     * 发送POST请求
     *
     * @param uri 请求的URI
     * @param queries 请求的查询参数
     * @param headers 请求的头部
     * @param body 请求的body参数
     * @param dataType 请求返回的数据类型引用
     * @param <T> 请求返回的数据类型
     * @return 返回请求的响应结果
     */
    public  <T> T post(String uri, Map<String, String> queries, Map<String, String> headers, Object body, TypeReference<T> dataType) {
        return post(uri, queries, headers, true, body, dataType);
    }

    /**
     * 发送POST请求
     *
     * @param uri 请求的URI
     * @param queries 请求的查询参数
     * @param headers 请求的头部
     * @param body 请求的body参数
     * @param dataType 请求返回的数据类型引用
     * @param <T> 请求返回的数据类型
     * @return 返回请求的响应结果
     */
    public  <T> T post(String uri, Map<String, String> queries, Map<String, String> headers, boolean addHeaderKeys, Object body, TypeReference<T> dataType) {
        return post(uri, queries, headers, addHeaderKeys, body, dataType,MediaType.APPLICATION_JSON);
    }

    /**
     * 发送一个POST请求
     * <pre>
     * {@code
     * // 示例，返回对象为ApiResponse<List<Apartment>>
     * ApiResponse<List<Apartment>> response = HttpUtils.post(uri,body,new TypeReference<ApiResponse<List<Apartment>>>() {});
     * }
     * </pre>
     * @param uri 请求的URI
     * @param queries 请求的查询参数
     * @param headers 请求的头部
     * @param addHeaderKeys 是否自动填充上游请求头中有keys或token到当前请求的请求头中
     * @param body 请求的body内容
     * @param dataType 返回的数据类型，如：new TypeReference<ApiResponse<List<Apartment>>>() {}
     * @param contentType 请求格式，如：MediaType.APPLICATION_JSON
     * @return 请求的响应结果
     */
    public  <T> T post(String uri, Map<String, String> queries, Map<String, String> headers, boolean addHeaderKeys, Object body, TypeReference<T> dataType,MediaType contentType) {
        Assert.isTrue(StringUtils.isNotEmpty(uri), "uri不能为空");
        Assert.isTrue(body != null, "请求的body不能为空");

        // 处理Query
        uri = getUriQuery(uri, queries);

        // 处理Header
        Consumer<org.springframework.http.HttpHeaders> headersConsumer = getHeadersConsumer(headers, addHeaderKeys);

        String json = webClientBuilder.build()
                .post()
                .uri(uri)
                .headers(headersConsumer)
                .contentType(contentType)
                .bodyValue(JSONObject.toJSONString(body))
                .retrieve()
                .bodyToMono(String.class)
                .block();
        return convertType(dataType, json);
    }


    /**
     * 发送put请求
     *
     * @param uri 请求的URI
     * @param body 请求的body参数
     * @return 返回请求的响应结果的
     */
    public  String put(String uri, Object body) {
        return put(uri, null, null, true, body, new TypeReference<String>() {});
    }

    /**
     * 发送put请求
     *
     * @param uri 请求的URI
     * @param body 请求的body参数
     * @param dataType 请求返回的数据类型引用
     * @param <T> 请求返回的数据类型
     * @return 返回请求的响应结果
     */
    public  <T> T put(String uri, Object body, TypeReference<T> dataType) {
        return put(uri, null, null, true, body, dataType);
    }

    /**
     * 发送put请求
     *
     * @param uri 请求的URI
     * @param queries 请求的查询参数
     * @param headers 请求的头部
     * @param body 请求的body参数
     * @param dataType 请求返回的数据类型引用
     * @param <T> 请求返回的数据类型
     * @return 返回请求的响应结果
     */
    public  <T> T put(String uri, Map<String, String> queries, Map<String, String> headers, Object body, TypeReference<T> dataType) {
        return put(uri, queries, headers, true, body, dataType);
    }

    /**
     * 向指定的URI发送PUT请求并返回响应结果
     * @param uri 要发送请求的URI
     * @param queries 请求的查询参数
     * @param headers 请求的头部信息
     * @param addHeaderKeys 是否添加原有头部字段的标志
     * @param body 请求的主体
     * @param dataType 请求返回结果的数据类型
     * @param <T> 请求返回结果的数据类型
     * @return 请求返回结果的类型转换后的对象
     */
    public  <T> T put(String uri, Map<String, String> queries, Map<String, String> headers, boolean addHeaderKeys, Object body, TypeReference<T> dataType) {
        return put(uri, queries, headers, addHeaderKeys, body, dataType, MediaType.APPLICATION_JSON);
    }

    /**
     * 向指定的URI发送PUT请求并返回响应结果
     * @param uri 要发送请求的URI
     * @param queries 请求的查询参数
     * @param headers 请求的头部信息
     * @param addHeaderKeys 是否添加原有头部字段的标志
     * @param body 请求的主体
     * @param dataType 请求返回结果的数据类型
     * @param <T> 请求返回结果的数据类型
     * @return 请求返回结果的类型转换后的对象
     */
    public  <T> T put(String uri, Map<String, String> queries, Map<String, String> headers, boolean addHeaderKeys, Object body, TypeReference<T> dataType,MediaType contentType) {
        // 检查URI是否为空
        Assert.isTrue(StringUtils.isNotEmpty(uri), "uri不能为空");
        // 检查请求主体是否为空
        Assert.isTrue(body != null, "请求的body不能为空");

        // 处理查询参数
        uri = getUriQuery(uri, queries);

        // 处理头部
        Consumer<org.springframework.http.HttpHeaders> headersConsumer = getHeadersConsumer(headers, addHeaderKeys);
        // 发送PUT请求并获取响应结果
        String json = webClientBuilder.build()
                .put()
                .uri(uri)
                .headers(headersConsumer)
                .contentType(MediaType.APPLICATION_JSON)
                .bodyValue(JSONObject.toJSONString(body))
                .retrieve()
                .bodyToMono(String.class)
                .block();
        // 转换结果数据类型并返回结果对象
        return convertType(dataType, json);
    }

    /**
     * 获取请求头消费者
     *
     * @param headers 请求头集合
     * @param addHeaderKeys 是否添加特殊请求头
     * @return 请求头消费者
     */
    private  Consumer<org.springframework.http.HttpHeaders> getHeadersConsumer(Map<String, String> headers, boolean addHeaderKeys) {
        return (httpHeaders -> {
            if (headers != null && !headers.isEmpty()) {
                for (Map.Entry<String, String> entry : headers.entrySet()) {
                    httpHeaders.put(entry.getKey(), Collections.singletonList(entry.getValue()));
                }
            }
        });
    }


    /**
     * 根据给定的URI和查询参数，返回添加查询参数的URI字符串
     *
     * @param uri       请求的URI
     * @param queries   查询参数的键值对集合
     * @return          添加查询参数后的URI字符串
     */
    private  String getUriQuery(String uri, Map<String, String> queries) {
        if (queries != null && !queries.isEmpty()) {
            if (!uri.contains("?")) {
                uri = uri + "?";
            }
            StringBuilder paramQuery = new StringBuilder();
            for (Map.Entry<String, String> entry : queries.entrySet()) {
                paramQuery.append("&").append(entry.getKey()).append("=").append(entry.getValue());
            }
            uri += paramQuery.substring(1);
        }
        return uri;
    }


    /**
     * 根据指定的数据类型和JSON字符串，将JSON字符串转换为指定的数据类型对象
     *
     * @param dataType  数据类型的类型引用
     * @param json  JSON字符串
     * @param <T>  数据类型
     * @return  转换后的数据类型对象
     */
    private  <T> T convertType(TypeReference<T> dataType, String json) {
        // 判断dataType类型是否为String
        if (dataType.getType() == String.class) {
            return (T) json;
        }
        return JSONObject.parseObject(json, dataType);
    }
}